﻿using DevExpress.Persistent.BaseImpl;
using EasyXaf.EntityDesigner.BusinessObjects.Entities.Associations;
using EasyXaf.EntityDesigner.BusinessObjects.Entities.EntityProperties;
using EasyXaf.EntityDesigner.Editors.LookupStringPropertyEditors;
using Humanizer;
using System.ComponentModel;

namespace EasyXaf.EntityDesigner.BusinessObjects.Entities;

/// <summary>
/// 实体
/// </summary>
public partial class Entity : BaseObject, IListBoxItemsProvider
{
    [Browsable(false)]
    public IList<EntityProperty> AllProperties
    {
        get
        {
            var properties = new List<EntityProperty>();
            if (BaseClass != null)
            {
                properties.AddRange(BaseClass.AllProperties);
            }
            properties.AddRange(Properties);
            return properties;
        }
    }

    [Browsable(false)]
    public IList<EntityProperty> UseableProperties
    {
        get
        {
            var excludedPropertyNames = typeof(BaseObject)
                .GetProperties()
                .Select(p => p.Name)
                .ToList();
            return AllProperties.Where(p => !excludedPropertyNames.Contains(p.Name)).ToList();
        }
    }

    private partial IList<Entity> GetBaseClassDataSource()
    {
        var entities = DomainModel.AllEntities.Where(entity =>
        {
            if (entity.AllowInheritable == DefaultBoolean.True)
            {
                return true;
            }
            else if (entity.AllowInheritable == DefaultBoolean.Default)
            {
                return entity.IsAbstract;
            }
            return false;
        });

        return [.. entities];
    }

    private partial IList<EntityProperty> GetDefaultPropertyDataSource()
    {
        var excludedPropertyTypes = new[]
        {
            PropertyType.Binary,
            PropertyType.Image,
            PropertyType.File,
            PropertyType.EntityCollection,
            PropertyType.Unknown
        };

        var properties = UseableProperties.Where(property =>
        {
            if (property.IsBrowsable == DefaultBoolean.False)
            {
                return false;
            }

            if (excludedPropertyTypes.Contains(property.PropertyType))
            {
                return false;
            }

            return true;
        });

        return [.. properties];
    }

    private static Entity FindEntity(double left, double top, DomainModel domainModel)
    {
        foreach (var entity in domainModel.VisualEntities)
        {
            var entityDesign = entity.GetEntityDesign(domainModel);
            if (entityDesign != null)
            {
                if (entityDesign.Left - 10 <= left
                    && entityDesign.Top - 10 <= top
                    && entityDesign.Left + 200 >= left
                    && entityDesign.Top + 100 >= top)
                {
                    return entity;
                }
            }
        }
        return null;
    }

    private partial string GetDisplayName()
    {
        if (!string.IsNullOrWhiteSpace(Caption))
        {
            return $"{Name}({Caption})";
        }
        return Name;
    }

    public string GetNamespace()
    {
        var @namespace = Namespace;
        if (string.IsNullOrWhiteSpace(@namespace))
        {
            @namespace = DomainModel.Namespace;
        }
        return @namespace;
    }

    public EntityDesign GetEntityDesign(DomainModel domainModel)
    {
        return EntityDesigns.FirstOrDefault(e => e.DomainModel == domainModel);
    }

    public void SetLocation(double[] viewportBounds, DomainModel domainModel)
    {
        var left = viewportBounds[0];
        var top = viewportBounds[1];
        var width = viewportBounds[2];
        var height = viewportBounds[3];

        left += (width - 200) / 10;
        top += (height - 100) / 10;

        while (true)
        {
            if (left + 400 > viewportBounds[0] + width)
            {
                left = viewportBounds[0] + (width - 200) / 10;
                top += 200;
            }

            var entity = FindEntity(left, top, domainModel);
            var ed = entity?.GetEntityDesign(domainModel);
            if (ed != null)
            {
                left = ed.Left + 300;
                top = ed.Top;
            }
            else
            {
                break;
            }
        }

        var entityDesign = GetEntityDesign(domainModel) ?? new EntityDesign(Session)
        {
            Entity = this,
            DomainModel = domainModel
        };

        entityDesign.Left = left;
        entityDesign.Top = top;
    }

    public Association CreateAssociation(Entity sourceEntity, EntityProperty sourceProperty, Entity targetEntity, EntityProperty targetProperty)
    {
        var association = new Association(Session)
        {
            Entity = this,
            Parent = targetEntity,
            Child = sourceEntity
        };

        if (targetProperty != null)
        {
            if (targetProperty.PropertyType == PropertyType.Entity || targetProperty.PropertyType == PropertyType.EntityCollection)
            {
                association.ParentProperty = targetProperty;
            }
        }

        if (sourceProperty != null)
        {
            if (sourceProperty.PropertyType == PropertyType.Entity || sourceProperty.PropertyType == PropertyType.EntityCollection)
            {
                association.ChildProperty = sourceProperty;
            }
        }

        var associationName = $"{targetEntity.Name}-{sourceEntity.Name.Pluralize()}";
        if (association.ChildProperty != null)
        {
            var childPropertyType = association.ChildProperty.PropertyType;

            if (association.ParentProperty != null)
            {
                var parentPropertyType = association.ParentProperty.PropertyType;

                if (parentPropertyType == PropertyType.Entity && childPropertyType == PropertyType.Entity)
                {
                    association.AssociationType = AssociationType.OneToOne;
                }
                else if (parentPropertyType == PropertyType.EntityCollection && childPropertyType == PropertyType.EntityCollection)
                {
                    association.AssociationType = AssociationType.ManyToMany;
                }

                associationName = $"{association.ParentProperty.Name}-{association.ChildProperty.Name}";
            }
            else
            {
                if (childPropertyType == PropertyType.Entity)
                {
                    associationName = $"{targetEntity.Name}-{association.ChildProperty.Name}";
                }
                else
                {
                    association.AssociationType = AssociationType.ManyToMany;
                    associationName = $"{targetEntity.Name.Pluralize()}-{association.ChildProperty.Name}";
                }
            }
        }
        else if (association.ParentProperty != null && association.ParentProperty.PropertyType == PropertyType.Entity)
        {
            association.AssociationType = AssociationType.OneToOne;
            associationName = $"{targetEntity.Name}-{sourceEntity.Name}";
        }

        association.Name = associationName;

        return association;
    }

    public EntityProperty FindPropertyByPath(string propertyPath)
    {
        var paths = propertyPath?.Split('.');
        var propertyName = paths?.FirstOrDefault();
        if (string.IsNullOrWhiteSpace(propertyName))
        {
            return null;
        }

        var property = Properties.FirstOrDefault(p => p.Name == propertyName);
        if (property == null)
        {
            return null;
        }

        if (paths.Length == 1)
        {
            return property;
        }
        else if (property.PropertyType != PropertyType.Entity || property.EntityType == null)
        {
            return null;
        }

        return property.EntityType.FindPropertyByPath(string.Join(".", paths.Skip(1)));
    }

    public IEnumerable<string> GetListBoxItems(string targetMemberName)
    {
        if (targetMemberName == nameof(Namespace))
        {
            var namespaces = DomainModel.Entities.Select(e => e.Namespace).ToList();
            namespaces.AddRange(DomainModel.Enums.Select(e => e.Namespace));
            namespaces.Add(DomainModel.Namespace);
            return namespaces
                .Where(ns => !string.IsNullOrWhiteSpace(ns))
                .Distinct()
                .OrderBy(ns => ns);
        }
        else if (targetMemberName == nameof(Category))
        {
            return DomainModel.Entities
                .Select(e => e.Category)
                .Where(c => !string.IsNullOrWhiteSpace(c))
                .Distinct()
                .OrderBy(n => n);
        }
        return [];
    }
}
